package com.quxiu.system.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.quxiu.common.core.exception.ServiceException;
import com.quxiu.common.core.utils.DateUtils;
import com.quxiu.system.api.domain.SysUser;
import com.quxiu.system.constant.*;
import com.quxiu.system.domain.*;
import com.quxiu.system.domain.vo.BpmApprovalVo;
import com.quxiu.system.domain.vo.BpmRollbackNodeVo;
import com.quxiu.system.mapper.*;
import com.quxiu.system.service.IBpmApprovalService;
import com.quxiu.system.service.IdService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

@Slf4j
@Service
public class BpmApprovalServiceImpl implements IBpmApprovalService {
    @Resource
    private BpmProcInstMapper bpmProcInstMapper;
    @Resource
    private BpmProcMapper bpmProcMapper;
    @Resource
    private BpmProcVersionMapper bpmProcVersionMapper;
    @Resource
    private BpmProcNodeMapper bpmProcNodeMapper;
    @Resource
    private SysUserMapper sysUserMapper;
    @Resource
    private IdService idService;
    @Resource
    private BpmFormDataMapper bpmFormDataMapper;
    @Resource
    private BpmProcInstNodeMapper bpmProcInstNodeMapper;
    @Resource
    private BpmApprovalRecordMapper bpmApprovalRecordMapper;
    @Resource
    private BpmProcActorMapper bpmProcActorMapper;


    @Override
    public void submitProcInst(Long procInstId) {
        BpmProcInst procInst = bpmProcInstMapper.selectInstByProcInstId(procInstId);
        if (procInst == null) {
            throw new ServiceException("流程实例不存在");
        }

        BpmProcInst updateParam = new BpmProcInst();
        updateParam.setProcInstId(procInst.getProcInstId());
        updateParam.setRunStatus(BpmInstRunStatus.RUNNING);
        updateParam.setApprovalStatus(BpmInstApprovalStatus.APPROVAL);
        updateParam.setSubmitTime(new Date());
        bpmProcInstMapper.updateStatus(updateParam);

        BpmProcInstNode startNode = bpmProcInstNodeMapper.getNodeByParentId(procInstId, 0L);
        bpmProcInstNodeMapper.updateStatus(startNode.getNodeId(), BpmInstNodeStatus.COMPLETED);

        BpmApprovalRecord startRecord = bpmApprovalRecordMapper.getStartNode(procInstId, startNode.getNodeId());
        startRecord.setHandleStatus(BpmApprovalRecordHandleStatus.COMPLETED);
        startRecord.setHandleResult(BpmApprovalRecordHandleResult.PASS);
        startRecord.setHandleTime(new Date());
        bpmApprovalRecordMapper.updateHandleResult(startRecord);

        this.addProcActor(procInstId, BpmProcActorType.APPLICAT, startRecord.getHandlerId(),
                BpmProcActorHandleStatus.HANDLED, startRecord.getRecordId());

        this.startNextNode(procInstId, startNode);

    }


    /**
     * 启动下一个节点
     * @param procInstId
     * @param node
     */
    private void startNextNode(Long procInstId, BpmProcInstNode node) {
        bpmProcInstNodeMapper.updateStatus(node.getNodeId(), BpmInstNodeStatus.COMPLETED);

        BpmProcInstNode nextNode = bpmProcInstNodeMapper.getNodeByParentId(procInstId, node.getNodeId());

        if (nextNode != null) {
            bpmProcInstNodeMapper.updateStatus(nextNode.getNodeId(), BpmInstNodeStatus.RUNNING);
            switch (nextNode.getNodeType()) {

                case BpmNodeType.START:
                    BpmApprovalRecord startRecord = bpmApprovalRecordMapper.selectByParentId(procInstId, nextNode.getNodeId(), 0L);
                    bpmApprovalRecordMapper.updateStatus(startRecord.getRecordId(), BpmApprovalRecordHandleStatus.RUNNING);

                    this.addProcActor(procInstId, BpmProcActorType.APPROVER, startRecord.getHandlerId(),
                            BpmProcActorHandleStatus.WAIT, startRecord.getRecordId());
                    break;
                case BpmNodeType.APPROVER:
                    BpmNodeConfig nodeConfig = JSONObject.parseObject(nextNode.getConfig(), BpmNodeConfig.class);
                    if (BpmHandleModel.SEQUENCE_SIGN == nodeConfig.getHandleModel()) {
                        BpmApprovalRecord record = bpmApprovalRecordMapper.selectByParentId(procInstId, nextNode.getNodeId(), 0L);
                        bpmApprovalRecordMapper.updateStatus(record.getRecordId(), BpmApprovalRecordHandleStatus.RUNNING);

                        this.addProcActor(procInstId, BpmProcActorType.APPROVER, record.getHandlerId(),
                                BpmProcActorHandleStatus.WAIT, record.getRecordId());

                        // 自动去重审批
                        this.autoApproval(procInstId);
                    } else {
                        List<BpmApprovalRecord> records = bpmApprovalRecordMapper.selectByNodeId(procInstId, nextNode.getNodeId());
                        for (BpmApprovalRecord record : records) {
                            bpmApprovalRecordMapper.updateStatus(record.getRecordId(), BpmApprovalRecordHandleStatus.RUNNING);
                            this.addProcActor(procInstId, BpmProcActorType.APPROVER, record.getHandlerId(),
                                    BpmProcActorHandleStatus.WAIT, record.getRecordId());
                        }
                    }

                    break;
                case BpmNodeType.NOTIFY:
                    List<BpmApprovalRecord> records = bpmApprovalRecordMapper.selectByNodeId(procInstId, nextNode.getNodeId());
                    for (BpmApprovalRecord record : records) {
                        BpmApprovalRecord result = new BpmApprovalRecord();
                        result.setRecordId(record.getRecordId());
                        result.setHandleStatus(BpmApprovalRecordHandleStatus.COMPLETED);
                        result.setHandleTime(new Date());
                        bpmApprovalRecordMapper.updateHandleResult(result);
                        this.addProcActor(procInstId, BpmProcActorType.NOTIFY, record.getHandlerId(),
                                BpmProcActorHandleStatus.WAIT, record.getRecordId());
                    }
                    startNextNode(procInstId, nextNode);
                    break;
                default:
                    throw new ServiceException("未知节点类型");
            }
        } else {
            BpmProcInst updateParam = new BpmProcInst();
            updateParam.setProcInstId(procInstId);
            updateParam.setRunStatus(BpmInstRunStatus.COMPLETED);
            updateParam.setApprovalStatus(BpmInstApprovalStatus.PASS);
            updateParam.setFinishTime(new Date());
            bpmProcInstMapper.updateStatus(updateParam);
        }

    }

    /**
     * 自动去重审批
     * @param procInstId
     */
    private void autoApproval(Long procInstId) {
        BpmProcInst procInst = bpmProcInstMapper.selectInstByProcInstId(procInstId);
        if (procInst == null) {
            throw new ServiceException("流程实例不存在");
        }
        BpmProcVersion version = bpmProcVersionMapper.selectByVersionId(procInst.getVersionId());
        if (version == null) {
            throw new ServiceException("流程版本不存在");
        }

        // 自动去重
        if (version.getAllowAutoApprove() == 1) {
            // 获取待审批人列表
            List<BpmProcActor> pendingApprovalActors = bpmProcActorMapper.selectPendingApprovalActors(procInstId);
            if (pendingApprovalActors != null && pendingApprovalActors.size() == 1) {
                BpmProcActor actor = pendingApprovalActors.get(0);
                BpmApprovalRecord record = bpmApprovalRecordMapper.selectByRecordId(actor.getHandleRecordId());
                BpmProcInstNode node = bpmProcInstNodeMapper.selectByNodeId(procInstId, record.getNodeId());
                BpmNodeConfig nodeConfig = JSONObject.parseObject(node.getConfig(), BpmNodeConfig.class);
                if (record.getJoinAutoApprove() == 1 && BpmHandleModel.SEQUENCE_SIGN == nodeConfig.getHandleModel() && BpmApprovalRecordHandleStatus.RUNNING.equals(record.getHandleStatus())) {
                    int approvedNum = bpmApprovalRecordMapper.selectApprovedNum(procInstId, record.getHandlerId());
                    if (approvedNum > 0) {
                        BpmApprovalVo params = new BpmApprovalVo();
                        params.setProcInstId(procInstId);
                        params.setRecordId(record.getRecordId());
                        params.setHandlerId(record.getHandlerId());
                        params.setHandleResult(BpmApprovalRecordHandleResult.PASS);
                        params.setHandleOpinion("系统自动通过");
                        this.approval(params);
                    }
                }
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approval(BpmApprovalVo params) {
        if (BpmApprovalRecordHandleResult.PASS.equals(params.getHandleResult())) { // 审批通过
            this.approvalForPass(params);
        } else if (BpmApprovalRecordHandleResult.REJECT.equals(params.getHandleResult())) { // 审批驳回
            this.approvalForReject(params);
        }
    }

    /**
     * 审批通过
     * @param params 参数
     */
    private void approvalForPass(BpmApprovalVo params) {
        BpmProcInst procInst = bpmProcInstMapper.selectInstByProcInstId(params.getProcInstId());
        if (procInst == null) {
            throw new ServiceException("流程实例不存在");
        }
        BpmApprovalRecord record = bpmApprovalRecordMapper.selectByRecordId(params.getRecordId());
        if (record == null) {
            throw new ServiceException("审批记录不存在");
        }
        if (!BpmApprovalRecordHandleStatus.RUNNING.equals(record.getHandleStatus())) {
            if (!BpmApprovalRecordHandleStatus.NOT_STARTED.equals(record.getHandleStatus())) {
                throw new ServiceException("当前审批不需要您处理或");
            }
        }
        BpmProcInstNode node = bpmProcInstNodeMapper.selectByNodeId(record.getProcInstId(), record.getNodeId());
        if (node == null) {
            throw new ServiceException("审批节点不存在");
        }

        BpmNodeConfig nodeConfig = JSONObject.parseObject(node.getConfig(), BpmNodeConfig.class);


        record.setHandleStatus(BpmApprovalRecordHandleStatus.COMPLETED);
        record.setHandleResult(BpmApprovalRecordHandleResult.PASS);
        record.setHandleOpinion(params.getHandleOpinion());
        record.setHandleTime(new Date());
        bpmApprovalRecordMapper.updateHandleResult(record);
        bpmProcActorMapper.updateHandleStatus(record.getProcInstId(), BpmProcActorType.APPROVER,
                params.getHandlerId(), BpmProcActorHandleStatus.HANDLED);

        // 更新表单数据
        if (params.getFormData() != null) {
            BpmFormData data = new BpmFormData();
            data.setProcInstId(params.getProcInstId());
            data.setData(params.getFormData().toJSONString());
            bpmFormDataMapper.update(data);
        }


        if (BpmNodeType.START.equals(record.getNodeType())) {
            // 启动下一个节点
            this.startNextNode(params.getProcInstId(), node);
        } else {
            if (BpmHandleModel.SEQUENCE_SIGN == nodeConfig.getHandleModel()) {
                // 如果当前节点是依次审批，则判断是否还有下一条审批记录
                BpmApprovalRecord nextRecord = bpmApprovalRecordMapper.selectByParentId(record.getProcInstId(), node.getNodeId(), record.getRecordId());
                if (nextRecord != null) {
                    bpmApprovalRecordMapper.updateStatus(nextRecord.getRecordId(), BpmApprovalRecordHandleStatus.RUNNING);
                    addProcActor(nextRecord.getProcInstId(), BpmProcActorType.APPROVER, nextRecord.getHandlerId(),
                            BpmProcActorHandleStatus.WAIT, nextRecord.getRecordId());

                    // 自动去重审批
                    this.autoApproval(params.getProcInstId());
                } else {
                    // 启动下一个节点
                    this.startNextNode(params.getProcInstId(), node);
                }

            } else if (BpmHandleModel.CONSENSUS_SIGN == nodeConfig.getHandleModel()) {
                // 如果当前节点是会签，则判断是否还有未完成的审批记录
                int unfinishedNum = bpmApprovalRecordMapper.selectUnfinishedNumByNodeId(procInst.getProcInstId(), node.getNodeId());
                if (unfinishedNum == 0) {
                    // 启动下一个节点
                    this.startNextNode(params.getProcInstId(), node);
                }
            } else if (BpmHandleModel.OR_SIGN == nodeConfig.getHandleModel()) {
                // 如果当前节点是或签，则将当前节点的其他审批者的审批记录改为抄送
                this.notifyOtherApprover(params, node, record);
                // 启动下一个节点
                this.startNextNode(params.getProcInstId(), node);
            }
        }


    }


    /**
     * 审批驳回
     * @param params 参数
     */
    private void approvalForReject(BpmApprovalVo params) {
        BpmProcInst procInst = bpmProcInstMapper.selectInstByProcInstId(params.getProcInstId());
        if (procInst == null) {
            throw new ServiceException("流程实例不存在");
        }

        BpmApprovalRecord record = bpmApprovalRecordMapper.selectByRecordId(params.getRecordId());
        if (record == null) {
            throw new ServiceException("审批记录不存在");
        }
        if (!BpmApprovalRecordHandleStatus.RUNNING.equals(record.getHandleStatus())) {
            if (!BpmApprovalRecordHandleStatus.NOT_STARTED.equals(record.getHandleStatus())) {
                throw new ServiceException("当前审批不需要您处理或已经处理过");
            }
        }
        BpmProcInstNode node = bpmProcInstNodeMapper.selectByNodeId(record.getProcInstId(), record.getNodeId());
        if (node == null) {
            throw new ServiceException("审批节点不存在");
        }

        BpmNodeConfig nodeConfig = JSONObject.parseObject(node.getConfig(), BpmNodeConfig.class);

        // 更新实例状态
        BpmProcInst updateParam = new BpmProcInst();
        updateParam.setProcInstId(params.getProcInstId());
        updateParam.setRunStatus(BpmInstRunStatus.COMPLETED);
        updateParam.setApprovalStatus(BpmInstApprovalStatus.REJECT);
        updateParam.setFinishTime(new Date());
        bpmProcInstMapper.updateStatus(updateParam);

        // 更新节点状态
        bpmProcInstNodeMapper.updateStatus(node.getNodeId(), BpmInstNodeStatus.COMPLETED);

        // 更新审批记录结果
        BpmApprovalRecord updateRecordParams = new BpmApprovalRecord();
        updateRecordParams.setRecordId(params.getRecordId());
        updateRecordParams.setHandleStatus(BpmApprovalRecordHandleStatus.COMPLETED);
        updateRecordParams.setHandleResult(BpmApprovalRecordHandleResult.REJECT);
        updateRecordParams.setHandleOpinion(params.getHandleOpinion());
        updateRecordParams.setHandleTime(new Date());
        bpmApprovalRecordMapper.updateHandleResult(updateRecordParams);

        bpmProcActorMapper.updateHandleStatus(params.getProcInstId(), BpmProcActorType.APPROVER, record.getHandlerId(), BpmProcActorHandleStatus.HANDLED);

        // 如果当前节点是会签、或签节点，则将当前节点的其他审批者的审批记录改为抄送
        if (BpmHandleModel.CONSENSUS_SIGN == nodeConfig.getHandleModel()
                || BpmHandleModel.OR_SIGN == nodeConfig.getHandleModel()) {
            this.notifyOtherApprover(params, node, record);
        }

        // 删除未开始的审批记录
        bpmApprovalRecordMapper.deleteForUnStart(params.getProcInstId());
        bpmProcInstNodeMapper.deleteForUnStart(params.getProcInstId());

    }

    /**
     * 抄送其他审批者
     * @param params
     * @param node
     * @param record
     */
    private void notifyOtherApprover(BpmApprovalVo params, BpmProcInstNode node, BpmApprovalRecord record) {
        List<BpmApprovalRecord> records = bpmApprovalRecordMapper.selectByNodeId(params.getProcInstId(), node.getNodeId());
        if (CollectionUtils.isNotEmpty(records)) {
            BpmProcInstNode nextNode = bpmProcInstNodeMapper.getNodeByParentId(params.getProcInstId(), node.getNodeId());
            Long notifyNodeId = idService.nextId();
            List<BpmApprovalRecord> notifyRecords = new ArrayList<>();
            List<BpmHandler> notifyHandlers = new ArrayList<>();

            String notifyNodeName = node.getNodeName();
            BpmNodeConfig nodeConfig = JSONObject.parseObject(node.getConfig(), BpmNodeConfig.class);
            if (BpmHandleModel.CONSENSUS_SIGN == nodeConfig.getHandleModel()) {
                notifyNodeName += "（抄送会签审批人）";
            } else if (BpmHandleModel.OR_SIGN == nodeConfig.getHandleModel()) {
                notifyNodeName += "（抄送或签审批人）";
            }

            for (BpmApprovalRecord item : records) {
                if (!item.getHandlerId().equals(params.getHandlerId())) {

                    this.addProcActor(params.getProcInstId(), BpmProcActorType.NOTIFY, item.getHandlerId(), BpmProcActorHandleStatus.WAIT, item.getRecordId());

                    BpmApprovalRecord notify = new BpmApprovalRecord();
                    notify.setRecordId(idService.nextId());
                    notify.setProcInstId(params.getProcInstId());
                    notify.setNodeId(notifyNodeId);
                    notify.setNodeType(BpmNodeType.NOTIFY);
                    notify.setNodeName(notifyNodeName);
                    notify.setParentId(0L);
                    notify.setHandlerId(item.getHandlerId());
                    notify.setHandlerName(item.getHandlerName());
                    notify.setHandleStatus(BpmApprovalRecordHandleStatus.COMPLETED);
                    notify.setHandleResult(BpmApprovalRecordHandleResult.PASS);
                    notify.setHandleTime(new Date());
                    notify.setCreateTime(new Date());
                    notifyRecords.add(notify);

                    BpmHandler handler = new BpmHandler(item.getHandlerId(), item.getHandlerName());
                    notifyHandlers.add(handler);

                    if (!item.getHandleStatus().equals(BpmApprovalRecordHandleStatus.COMPLETED)) {
                        bpmApprovalRecordMapper.deleteByRecordId(item.getRecordId());
                        int num = bpmApprovalRecordMapper.selectApprovedNum(params.getProcInstId(), item.getHandlerId());
                        if (num == 0) {
                            bpmProcActorMapper.delete(params.getProcInstId(), BpmProcActorType.APPROVER, item.getHandlerId());
                        } else {
                            bpmProcActorMapper.updateHandleStatus(params.getProcInstId(), BpmProcActorType.APPROVER, item.getHandlerId(), BpmProcActorHandleStatus.HANDLED);
                        }
                    }
                }
            }

            BpmProcInstNode notifyNode = new BpmProcInstNode();
            notifyNode.setNodeId(notifyNodeId);
            notifyNode.setProcInstId(params.getProcInstId());
            notifyNode.setParentId(node.getNodeId());
            notifyNode.setNodeType(BpmNodeType.NOTIFY);
            notifyNode.setNodeName(notifyNodeName);
            notifyNode.setConfig(node.getConfig());
            notifyNode.setStatus(BpmInstNodeStatus.COMPLETED);
            notifyNode.setHandlers(JSONObject.toJSONString(notifyHandlers));
            notifyNode.setOriginNodeId(node.getNodeId());
            bpmProcInstNodeMapper.insert(notifyNode);

            if (nextNode != null) {
                bpmProcInstNodeMapper.updateParentId(nextNode.getNodeId(), notifyNodeId);
            }

            bpmApprovalRecordMapper.insertBatch(notifyRecords);
        }
    }

    private void addProcActor(Long procInstId, String type, Long userId, String handleStatus, Long recordId) {
        BpmProcActor oldActor = bpmProcActorMapper.select(procInstId, type, userId);
        if (oldActor == null) {
            BpmProcActor actor = new BpmProcActor();
            actor.setActorId(idService.nextId());
            actor.setProcInstId(procInstId);
            actor.setType(type);
            actor.setUserId(userId);
            actor.setHandleStatus(handleStatus);
            actor.setHandleRecordId(recordId);
            bpmProcActorMapper.insert(actor);
        } else {
            oldActor.setHandleStatus(handleStatus);
            oldActor.setHandleRecordId(recordId);
            bpmProcActorMapper.update(oldActor);
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void revoke(BpmApprovalVo params) {
        BpmProcInst procInst = bpmProcInstMapper.selectInstByProcInstId(params.getProcInstId());
        if (procInst == null) {
            throw new ServiceException("流程实例不存在");
        }

        if (!BpmInstRunStatus.RUNNING.equals(procInst.getRunStatus())) {
            throw new ServiceException("流程未在运行中, 不能进行撤消操作");
        }

        BpmProcVersion version = bpmProcVersionMapper.selectByVersionId(procInst.getVersionId());
        if (version.getAllowRevoke() != 1) {
            throw new ServiceException("流程不允许撤消操作");
        }

        if (!procInst.getOwnerId().equals(params.getHandlerId())) {
            throw new ServiceException("只有申请人才能进行撤消操作!");
        }

        BpmProcInstNode node = bpmProcInstNodeMapper.getRunningNode(params.getProcInstId());
        if (node == null) {
            throw new ServiceException("审批节点不存在");
        }

        List<BpmApprovalRecord> records = bpmApprovalRecordMapper.selectByNodeId(params.getProcInstId(), node.getNodeId());
        if (CollectionUtils.isNotEmpty(records)) {
            for (BpmApprovalRecord item : records) {
                bpmApprovalRecordMapper.deleteByRecordId(item.getRecordId());
                int num = bpmApprovalRecordMapper.selectApprovedNum(params.getProcInstId(), item.getHandlerId());
                if (num == 0) {
                    bpmProcActorMapper.delete(params.getProcInstId(), BpmProcActorType.APPROVER, item.getHandlerId());
                } else {
                    bpmProcActorMapper.updateHandleStatus(params.getProcInstId(), BpmProcActorType.APPROVER, item.getHandlerId(), BpmProcActorHandleStatus.HANDLED);
                }
            }
        }
        // 插入撤消记录
        BpmApprovalRecord record = new BpmApprovalRecord();
        record.setRecordId(idService.nextId());
        record.setProcInstId(params.getProcInstId());
        record.setNodeId(node.getNodeId());
        record.setNodeType(node.getNodeType());
        record.setNodeName(node.getNodeName());
        record.setParentId(0L);
        record.setHandlerId(params.getHandlerId());
        SysUser user = sysUserMapper.selectUserById(params.getHandlerId());
        if (user != null) {
            record.setHandlerName(user.getNickName());
        }
        record.setHandleStatus(BpmApprovalRecordHandleStatus.COMPLETED);
        record.setHandleResult(BpmApprovalRecordHandleResult.REVOKE);
        record.setHandleOpinion("撤消申请");
        record.setHandleTime(new Date());
        record.setCreateTime(new Date());
        bpmApprovalRecordMapper.insert(record);

        // 更新实例状态
        BpmProcInst updateParam = new BpmProcInst();
        updateParam.setProcInstId(params.getProcInstId());
        updateParam.setRunStatus(BpmInstRunStatus.COMPLETED);
        updateParam.setApprovalStatus(BpmInstApprovalStatus.REVOKE);
        updateParam.setFinishTime(new Date());
        bpmProcInstMapper.updateStatus(updateParam);

        // 更新节点状态
        bpmProcInstNodeMapper.updateStatus(node.getNodeId(), BpmInstNodeStatus.COMPLETED);



        // 删除未开始的审批记录
        bpmApprovalRecordMapper.deleteForUnStart(params.getProcInstId());
        bpmProcInstNodeMapper.deleteForUnStart(params.getProcInstId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addSign(BpmApprovalVo params) {
        BpmProcInst procInst = bpmProcInstMapper.selectInstByProcInstId(params.getProcInstId());
        if (procInst == null) {
            throw new ServiceException("流程实例不存在");
        }

        if (!BpmInstRunStatus.RUNNING.equals(procInst.getRunStatus())) {
            throw new ServiceException("流程未在运行中, 不能进行加签操作");
        }

        BpmProcVersion version = bpmProcVersionMapper.selectByVersionId(procInst.getVersionId());
        if (version.getAllowAddSign() != 1) {
            throw new ServiceException("流程不允许加签操作");
        }

        BpmApprovalRecord record = bpmApprovalRecordMapper.selectByRecordId(params.getRecordId());
        if (record == null) {
            throw new ServiceException("审批记录不存在");
        }
        if (!record.getHandleStatus().equals(BpmApprovalRecordHandleStatus.RUNNING)) {
            throw new ServiceException("当前流程无效");
        }
        if (!record.getHandlerId().equals(params.getHandlerId())) {
            throw new ServiceException("当前用户不是待审批人");
        }
        BpmProcInstNode node = bpmProcInstNodeMapper.selectByNodeId(record.getProcInstId(), record.getNodeId());
        BpmNodeConfig config = JSONObject.parseObject(node.getConfig(), BpmNodeConfig.class);
        if (BpmHandleModel.SEQUENCE_SIGN != config.getHandleModel()) {
            throw new ServiceException("会签/或签审批不能进行加签操作");
        }

        SysUser user = sysUserMapper.selectUserById(params.getToUserId());
        if (user == null) {
            throw new ServiceException("加签用户不存在");
        }
        params.setToUserName(user.getNickName());

        if (BpmAddSignType.BEFORE.equals(params.getAddSignType())) {
            this.addSingByBefore(params, version, record);
        } else if (BpmAddSignType.AFTER.equals(params.getAddSignType())) {
            this.addSingByAfter(params, version, record);
        } else {
            throw new ServiceException("加签类型错误");
        }

    }

    /**
     * 前加签
     */
    private void addSingByBefore(BpmApprovalVo params, BpmProcVersion version, BpmApprovalRecord record) {
        // 获取下一个记录
        BpmApprovalRecord afterRecord = bpmApprovalRecordMapper.selectByParentId(params.getProcInstId(), record.getNodeId(), record.getRecordId());

        // 更新审批记录结果
        BpmApprovalRecord updateRecordParams = new BpmApprovalRecord();
        updateRecordParams.setRecordId(params.getRecordId());
        updateRecordParams.setHandleStatus(BpmApprovalRecordHandleStatus.COMPLETED);
        updateRecordParams.setHandleResult(BpmApprovalRecordHandleResult.ADD_SIGN);
        updateRecordParams.setHandleOpinion(params.getRemark());
        updateRecordParams.setAddSignType(params.getAddSignType());
        updateRecordParams.setToUserId(params.getToUserId());
        updateRecordParams.setToUserName(params.getToUserName());
        updateRecordParams.setHandleTime(new Date());
        bpmApprovalRecordMapper.updateHandleResult(updateRecordParams);
        addProcActor(record.getProcInstId(), BpmProcActorType.APPROVER, record.getHandlerId(), BpmProcActorHandleStatus.HANDLED, record.getRecordId());

        // 加签记录
        BpmApprovalRecord signRecord = new BpmApprovalRecord();
        signRecord.setRecordId(idService.nextId());
        signRecord.setProcInstId(record.getProcInstId());
        signRecord.setNodeId(record.getNodeId());
        signRecord.setNodeType(record.getNodeType());
        signRecord.setNodeName(record.getNodeName());
        signRecord.setParentId(record.getRecordId());
        signRecord.setHandlerId(params.getToUserId());
        signRecord.setHandlerName(params.getToUserName());
        signRecord.setHandleStatus(BpmApprovalRecordHandleStatus.RUNNING);
        signRecord.setJoinAutoApprove(version.getAllowAutoApprove() != null ? version.getAllowAutoApprove() : 0);
        signRecord.setCreateTime(new Date());
        bpmApprovalRecordMapper.insert(signRecord);
        addProcActor(record.getProcInstId(), BpmProcActorType.APPROVER, signRecord.getHandlerId(), BpmProcActorHandleStatus.WAIT, signRecord.getRecordId());

        // 添加当前审批人再次审批记录
        BpmApprovalRecord addRecord = new BpmApprovalRecord();
        addRecord.setRecordId(idService.nextId());
        addRecord.setProcInstId(record.getProcInstId());
        addRecord.setNodeId(record.getNodeId());
        addRecord.setNodeType(record.getNodeType());
        addRecord.setNodeName(record.getNodeName());
        addRecord.setParentId(signRecord.getRecordId());
        addRecord.setHandlerId(record.getHandlerId());
        addRecord.setHandlerName(record.getHandlerName());
        addRecord.setHandleStatus(BpmApprovalRecordHandleStatus.NOT_STARTED);
        addRecord.setJoinAutoApprove(version.getAllowAutoApprove() != null ? version.getAllowAutoApprove() : 0);
        addRecord.setCreateTime(new Date());
        bpmApprovalRecordMapper.insert(addRecord);

        if (afterRecord != null) {
            // 更新下一个记录的父节点
            bpmApprovalRecordMapper.updateParentId(afterRecord.getRecordId(), addRecord.getRecordId());
        }
    }

    /**
     * 后加签
     */
    private void addSingByAfter(BpmApprovalVo params, BpmProcVersion version, BpmApprovalRecord record) {
        // 获取下一个记录
        BpmApprovalRecord afterRecord = bpmApprovalRecordMapper.selectByParentId(params.getProcInstId(), record.getNodeId(), record.getRecordId());

        // 更新审批记录结果
        BpmApprovalRecord updateRecordParams = new BpmApprovalRecord();
        updateRecordParams.setRecordId(params.getRecordId());
        updateRecordParams.setHandleStatus(BpmApprovalRecordHandleStatus.COMPLETED);
        updateRecordParams.setHandleResult(BpmApprovalRecordHandleResult.PASS);
        updateRecordParams.setHandleOpinion(params.getHandleOpinion());
        updateRecordParams.setHandleTime(new Date());
        bpmApprovalRecordMapper.updateHandleResult(updateRecordParams);

        // 添加当前审批人加签记录
        BpmApprovalRecord addRecord = new BpmApprovalRecord();
        addRecord.setRecordId(idService.nextId());
        addRecord.setProcInstId(record.getProcInstId());
        addRecord.setNodeId(record.getNodeId());
        addRecord.setNodeType(record.getNodeType());
        addRecord.setNodeName(record.getNodeName());
        addRecord.setParentId(record.getRecordId());
        addRecord.setHandlerId(record.getHandlerId());
        addRecord.setHandlerName(record.getHandlerName());
        addRecord.setHandleStatus(BpmApprovalRecordHandleStatus.COMPLETED);
        addRecord.setHandleResult(BpmApprovalRecordHandleResult.ADD_SIGN);
        addRecord.setHandleOpinion(params.getRemark());
        addRecord.setAddSignType(params.getAddSignType());
        addRecord.setToUserId(params.getToUserId());
        addRecord.setToUserName(params.getToUserName());
        addRecord.setHandleTime(new Date());
        addRecord.setJoinAutoApprove(0);
        addRecord.setCreateTime(new Date());
        bpmApprovalRecordMapper.insert(addRecord);
        addProcActor(record.getProcInstId(), BpmProcActorType.APPROVER, addRecord.getHandlerId(),
                BpmProcActorHandleStatus.HANDLED, addRecord.getRecordId());

        // 添加被加签人记录
        BpmApprovalRecord signRecord = new BpmApprovalRecord();
        signRecord.setRecordId(idService.nextId());
        signRecord.setProcInstId(record.getProcInstId());
        signRecord.setNodeId(record.getNodeId());
        signRecord.setNodeType(record.getNodeType());
        signRecord.setNodeName(record.getNodeName());
        signRecord.setParentId(addRecord.getRecordId());
        signRecord.setHandlerId(params.getToUserId());
        signRecord.setHandlerName(params.getToUserName());
        signRecord.setHandleStatus(BpmApprovalRecordHandleStatus.RUNNING);
        signRecord.setJoinAutoApprove(version.getAllowAutoApprove() != null ? version.getAllowAutoApprove() : 0);
        signRecord.setCreateTime(new Date());
        bpmApprovalRecordMapper.insert(signRecord);
        addProcActor(record.getProcInstId(), BpmProcActorType.APPROVER, signRecord.getHandlerId(),
                BpmProcActorHandleStatus.WAIT, signRecord.getRecordId());

        if (afterRecord != null) {
            // 更新下一个记录的父节点
            bpmApprovalRecordMapper.updateParentId(afterRecord.getRecordId(), signRecord.getRecordId());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void forward(BpmApprovalVo params, boolean isAdmin) {
        BpmApprovalRecord record = bpmApprovalRecordMapper.selectByRecordId(params.getRecordId());
        if (record == null) {
            throw new ServiceException("审批记录不存在");
        }

        params.setProcInstId(record.getProcInstId());
        BpmProcInst procInst = bpmProcInstMapper.selectInstByProcInstId(params.getProcInstId());
        if (procInst == null) {
            throw new ServiceException("流程实例不存在");
        }

        if (!BpmInstRunStatus.RUNNING.equals(procInst.getRunStatus())) {
            throw new ServiceException("流程未在运行中, 不能进行加签操作");
        }

        BpmProcVersion version = bpmProcVersionMapper.selectByVersionId(procInst.getVersionId());
        if (version.getAllowForward() != 1) {
            throw new ServiceException("流程不允许转交操作");
        }

        if (!record.getHandleStatus().equals(BpmApprovalRecordHandleStatus.RUNNING)) {
            throw new ServiceException("当前流程无效");
        }
        if (!isAdmin && !record.getHandlerId().equals(params.getHandlerId())) {
            throw new ServiceException("当前用户不是待审批人");
        }
        BpmProcInstNode node = bpmProcInstNodeMapper.selectByNodeId(record.getProcInstId(), record.getNodeId());
        BpmNodeConfig config = JSONObject.parseObject(node.getConfig(), BpmNodeConfig.class);

        SysUser user = sysUserMapper.selectUserById(params.getToUserId());
        if (user == null) {
            throw new ServiceException("转交用户不存在");
        }
        params.setToUserName(user.getNickName());

        // 获取下一个记录
        BpmApprovalRecord afterRecord = bpmApprovalRecordMapper.selectByParentId(params.getProcInstId(), record.getNodeId(), record.getRecordId());

        // 更新审批记录结果
        BpmApprovalRecord updateRecordParams = new BpmApprovalRecord();
        updateRecordParams.setRecordId(params.getRecordId());
        updateRecordParams.setHandleStatus(BpmApprovalRecordHandleStatus.COMPLETED);
        updateRecordParams.setHandleResult(BpmApprovalRecordHandleResult.FORWARD);
        updateRecordParams.setHandleOpinion(params.getRemark());
        updateRecordParams.setAddSignType(params.getAddSignType());
        updateRecordParams.setToUserId(params.getToUserId());
        updateRecordParams.setToUserName(params.getToUserName());
        updateRecordParams.setHandleTime(new Date());
        bpmApprovalRecordMapper.updateHandleResult(updateRecordParams);
        addProcActor(record.getProcInstId(), BpmProcActorType.APPROVER, record.getHandlerId(), BpmProcActorHandleStatus.HANDLED, record.getRecordId());

        // 创建转交人审批记录
        BpmApprovalRecord forwardRecord = new BpmApprovalRecord();
        forwardRecord.setRecordId(idService.nextId());
        forwardRecord.setProcInstId(record.getProcInstId());
        forwardRecord.setNodeId(record.getNodeId());
        forwardRecord.setNodeType(record.getNodeType());
        forwardRecord.setNodeName(record.getNodeName());
        forwardRecord.setParentId(record.getRecordId());
        forwardRecord.setHandlerId(params.getToUserId());
        forwardRecord.setHandlerName(params.getToUserName());
        forwardRecord.setHandleStatus(BpmApprovalRecordHandleStatus.RUNNING);
        forwardRecord.setJoinAutoApprove(version.getAllowAutoApprove() != null ? version.getAllowAutoApprove() : 0);
        forwardRecord.setCreateTime(new Date());
        bpmApprovalRecordMapper.insert(forwardRecord);
        addProcActor(record.getProcInstId(), BpmProcActorType.APPROVER, forwardRecord.getHandlerId(), BpmProcActorHandleStatus.WAIT, forwardRecord.getRecordId());

        if (afterRecord != null && BpmHandleModel.SEQUENCE_SIGN == config.getHandleModel()) {
            // 更新下一个记录的父节点
            bpmApprovalRecordMapper.updateParentId(afterRecord.getRecordId(), forwardRecord.getRecordId());
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void forwardByAdmin(BpmApprovalVo params) {
        SysUser admin = sysUserMapper.selectUserById(params.getHandlerId());
        if (admin == null) {
            throw new ServiceException("管理员用户不存在");
        }
        SysUser toUser = sysUserMapper.selectUserById(params.getToUserId());
        if (toUser == null) {
            throw new ServiceException("转交用户不存在");
        }
        params.setToUserName(toUser.getNickName());
        params.setRemark(String.format("管理员[%s]将审批转交给%s", admin.getNickName(), toUser.getNickName()));
        if (CollectionUtils.isNotEmpty(params.getRecordIds())) {
            for (Long recordId : params.getRecordIds()) {
                params.setRecordId(recordId);
                this.forward(params, true);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rollback(BpmApprovalVo params) {
        BpmProcInst procInst = bpmProcInstMapper.selectInstByProcInstId(params.getProcInstId());
        if (procInst == null) {
            throw new ServiceException("流程实例不存在");
        }

        if (!BpmInstRunStatus.RUNNING.equals(procInst.getRunStatus())) {
            throw new ServiceException("流程未在运行中, 不能进行加签操作");
        }

        BpmProcVersion version = bpmProcVersionMapper.selectByVersionId(procInst.getVersionId());
        if (version.getAllowAddSign() != 1) {
            throw new ServiceException("流程不允许加签操作");
        }

        BpmApprovalRecord record = bpmApprovalRecordMapper.selectByRecordId(params.getRecordId());
        if (record == null) {
            throw new ServiceException("审批记录不存在");
        }
        if (!record.getHandleStatus().equals(BpmApprovalRecordHandleStatus.RUNNING)) {
            throw new ServiceException("当前流程无效");
        }
        if (!record.getHandlerId().equals(params.getHandlerId())) {
            throw new ServiceException("当前用户不是待审批人");
        }
        BpmProcInstNode node = bpmProcInstNodeMapper.selectByNodeId(record.getProcInstId(), record.getNodeId());
        BpmNodeConfig config = JSONObject.parseObject(node.getConfig(), BpmNodeConfig.class);
        if (BpmHandleModel.SEQUENCE_SIGN != config.getHandleModel()) {
            throw new ServiceException("会签/或签审批不能进行回退操作");
        }

        BpmProcInstNode rollbackNode = bpmProcInstNodeMapper.selectByNodeId(record.getProcInstId(), record.getNodeId());
        if (rollbackNode == null) {
            throw new ServiceException("回退节点不存在");
        }

        // 更新审批记录结果
        BpmApprovalRecord updateRecordParams = new BpmApprovalRecord();
        updateRecordParams.setRecordId(params.getRecordId());
        updateRecordParams.setHandleStatus(BpmApprovalRecordHandleStatus.COMPLETED);
        updateRecordParams.setHandleResult(BpmApprovalRecordHandleResult.ROLLBACK);
        updateRecordParams.setHandleOpinion(params.getHandleOpinion());
        updateRecordParams.setToNodeId(rollbackNode.getNodeId());
        updateRecordParams.setToNodeName(rollbackNode.getNodeName());
        updateRecordParams.setHandleTime(new Date());
        bpmApprovalRecordMapper.updateHandleResult(updateRecordParams);
        addProcActor(record.getProcInstId(), BpmProcActorType.APPROVER, record.getHandlerId(), BpmProcActorHandleStatus.HANDLED, record.getRecordId());

        // 删除未开始的节点记录
        bpmProcInstNodeMapper.deleteForUnStart(params.getProcInstId());
        bpmApprovalRecordMapper.deleteForUnStart(params.getProcInstId());

        // 将旧记录禁用自动审批
        bpmApprovalRecordMapper.disableJoinAutoApprove(params.getProcInstId());

        // 生成新节点记录
        List<BpmProcInstNode> sourceNodes = JSON.parseArray(procInst.getNodeConfig(), BpmProcInstNode.class);
        if (CollectionUtils.isNotEmpty(sourceNodes)) {
            int index = 0;
            for (; index < sourceNodes.size(); index++) {
                BpmProcInstNode sourceNode = sourceNodes.get(index);
                if (params.getToNodeId().equals(sourceNode.getNodeId())) {
                    break;
                }
            }
            List<BpmProcInstNode> newNodes = sourceNodes.subList(index, sourceNodes.size());
            if (CollectionUtils.isNotEmpty(newNodes)) {
                List<BpmProcInstNode> instNodeList = new ArrayList<>();
                List<BpmApprovalRecord> records = new ArrayList<>();
                Long nodeParentId = node.getNodeId();
                for (int i=0; i < newNodes.size(); i++) {
                    BpmProcInstNode newNode = newNodes.get(i);
                    BpmNodeConfig nodeConfig = JSONObject.parseObject(newNode.getConfig(), BpmNodeConfig.class);
                    BpmProcInstNode instNode = new BpmProcInstNode();
                    instNode.setNodeId(idService.nextId());
                    instNode.setParentId(nodeParentId);
                    instNode.setProcInstId(procInst.getProcInstId());
                    instNode.setNodeType(newNode.getNodeType());
                    instNode.setNodeName(newNode.getNodeName());
                    instNode.setStatus(BpmInstNodeStatus.NOT_STARTED);
                    instNode.setConfig(newNode.getConfig());
                    instNode.setHandlers(newNode.getHandlers());
                    instNode.setOriginNodeId(newNode.getNodeId());
                    instNodeList.add(instNode);

                    nodeParentId = instNode.getNodeId();

                    long recordParentId = 0L;
                    List<BpmHandler> handlerList = JSON.parseArray(newNode.getHandlers(), BpmHandler.class);
                    for (BpmHandler handler : handlerList) {
                        BpmApprovalRecord newRecord = new BpmApprovalRecord();
                        newRecord.setRecordId(idService.nextId());
                        newRecord.setProcInstId(procInst.getProcInstId());
                        newRecord.setParentId(recordParentId);
                        newRecord.setNodeId(instNode.getNodeId());
                        newRecord.setNodeType(instNode.getNodeType());
                        newRecord.setNodeName(instNode.getNodeName());
                        newRecord.setHandlerId(handler.getUserId());
                        newRecord.setHandlerName(handler.getName());
                        newRecord.setHandleStatus(BpmApprovalRecordHandleStatus.NOT_STARTED);
                        if (BpmNodeType.APPROVER.equals(instNode.getNodeType()) && version.getAllowAutoApprove() != null) {
                            newRecord.setJoinAutoApprove(version.getAllowAutoApprove());
                        } else {
                            newRecord.setJoinAutoApprove(0);
                        }
                        newRecord.setCreateTime(DateUtils.getNowDate());
                        records.add(newRecord);

                        if (BpmNodeType.APPROVER.equals(instNode.getNodeType())
                                && BpmHandleModel.SEQUENCE_SIGN == nodeConfig.getHandleModel()) {
                            recordParentId = newRecord.getRecordId();
                        }
                    }
                }

                bpmProcInstNodeMapper.insertBatch(instNodeList);
                bpmApprovalRecordMapper.insertBatch(records);
            }
        }
        // 启动下一节点
        startNextNode(params.getProcInstId(), node);
    }

    @Override
    public List<BpmRollbackNodeVo> getRollbackNodes(BpmApprovalVo params) {
        BpmProcInst procInst = bpmProcInstMapper.selectInstByProcInstId(params.getProcInstId());
        if (procInst == null) {
            throw new ServiceException("流程实例不存在");
        }

        if (!BpmInstRunStatus.RUNNING.equals(procInst.getRunStatus())) {
            throw new ServiceException("流程未在运行中, 不能进行加签操作");
        }

        BpmProcVersion version = bpmProcVersionMapper.selectByVersionId(procInst.getVersionId());
        if (version.getAllowAddSign() != 1) {
            throw new ServiceException("流程不允许加签操作");
        }

        BpmApprovalRecord record = bpmApprovalRecordMapper.selectByRecordId(params.getRecordId());
        if (record == null) {
            throw new ServiceException("审批记录不存在");
        }
        if (!record.getHandleStatus().equals(BpmApprovalRecordHandleStatus.RUNNING)) {
            throw new ServiceException("当前流程无效");
        }
        BpmProcInstNode node = bpmProcInstNodeMapper.selectByNodeId(procInst.getProcInstId(), record.getNodeId());
        if (!BpmNodeType.APPROVER.equals(node.getNodeType())) {
            throw new ServiceException("无效的节点");
        }

        List<BpmRollbackNodeVo> result = new ArrayList<>();
        List<BpmProcInstNode> sourceNodes = JSON.parseArray(procInst.getNodeConfig(), BpmProcInstNode.class);
        if (CollectionUtils.isNotEmpty(sourceNodes)) {
            for (int i=0; i<sourceNodes.size(); i++) {
                BpmProcInstNode sourceNode = sourceNodes.get(i);
                if (node.getOriginNodeId().equals(sourceNode.getNodeId())) {
                    break;
                }
                if (BpmNodeType.NOTIFY.equals(sourceNode.getNodeType())) {
                    continue;
                }
                BpmNodeConfig sourceNodeConfig = JSONObject.parseObject(sourceNode.getConfig(), BpmNodeConfig.class);
                BpmRollbackNodeVo rollbackNode = new BpmRollbackNodeVo();
                rollbackNode.setNodeId(sourceNode.getNodeId());
                rollbackNode.setNodeName(sourceNode.getNodeName());
                rollbackNode.setNodeType(sourceNode.getNodeType());

                StringBuilder describe = new StringBuilder();
                List<BpmHandler> handlers = JSON.parseArray(sourceNode.getHandlers(), BpmHandler.class);
                if (CollectionUtils.isNotEmpty(handlers)) {
                    for (int j=0; j<handlers.size(); j++) {
                        BpmHandler handler = handlers.get(j);
                        describe.append(handler.getName()).append("、");
                        if (j == 1) {
                            break;
                        }
                    }
                    if (describe.length() > 0) {
                        describe.deleteCharAt(describe.length() - 1);
                    }

                    if (handlers.size() > 1) {
                        if (handlers.size() > 2) {
                            describe.append("等");
                        }
                        if (BpmHandleModel.SEQUENCE_SIGN == sourceNodeConfig.getHandleModel()) {
                            describe.append(handlers.size()).append("人依次审批");
                        } else if (BpmHandleModel.CONSENSUS_SIGN == sourceNodeConfig.getHandleModel()) {
                            describe.append(handlers.size()).append("人会签");
                        } else if (BpmHandleModel.OR_SIGN == sourceNodeConfig.getHandleModel()) {
                            describe.append(handlers.size()).append("人或签");
                        }
                    }

                    rollbackNode.setDescribe(describe.toString());
                }

                result.add(rollbackNode);
            }
        }
        if (!result.isEmpty()) {
            BpmRollbackNodeVo lastNode = result.get(result.size() - 1);
            lastNode.setNodeName(lastNode.getNodeName() + "(上一步)");
        }
        Collections.reverse(result);
        return result;
    }
}
